/*
 * Created on 2005/05/17
 * Author aki@www.xucker.jpn.org
 * License Apache2.0 or Common Public License
 */
package jp.sourceforge.akjrcp.ui.draw2d;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.StackLayout;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.jpn.xucker.commons.audio.WaveFormUtils;
import org.jpn.xucker.commons.audio.WaveUtils;
import org.jpn.xucker.commons.audio.WaveFormUtils.Canceller;

/**
 * 
 *
 */
public class WaveFormFigureCanvas extends Canvas{
    
    public static Log log = LogFactory.getLog(WaveFormFigureCanvas.class);    
    public static final int NORMAL=0;
    private int mode=NORMAL;
    
    private double hzoom=1.0;
    private double vzoom=0.5;
    
    private LocationFigure locationFigure;
    private EmptyFigure rootFigure;

    private ChainedFlowFigure flowFigure; //root of waveforms
    
    private DraggableAlaphaLayerFigure selectionFigure;
    private AlphaLayerFigure selectionRectangleFigure;
    
 
    private Rectangle selectRectangle;
    private long selectionStart;
    private long selectionEnd;
    private long current;
    
    private LightweightSystem lws;
    
    private DefaultZoom zoom=new DefaultZoom();
    
    private WaveFormListener waveFormListener;
    
    public Figure getBackgroundFigure() {
        return backgroundFigure;
    }
    public void setBackgroundFigure(Figure backgroundFigure) {
        this.backgroundFigure = backgroundFigure;
    }
    
    public Figure getForegroundFigure() {
        return foregroundFigure;
    }
    public void setForegroundFigure(Figure foregroundFigure) {
        this.foregroundFigure = foregroundFigure;
    }
    private int autoScrollMargin=15;//30,now bugged
    
    double splitframe=0.01;
    
    Shell shell;
    
    private int baseMemoryPixel=100;//second pixel?label pixel;
    
    private int perFigure=256*8;
    private DoubleScalableFigure resisableFigure;
    private int waveFormHeight=256;
    private int panelHeight;
    
    private long maxAudioTime;//audio time
    
    private int sampleRate;
    public class SelectionFigure extends AlphaLayerFigure{

        /**
         * @param color
         * @param alpha
         */
        public SelectionFigure(Color color, int alpha) {
            super(color, alpha);
           
           
        }
        
        public void setScale(double w,double h){
            if(selectionFigure.isVisible()){
            setSelectRealValue(selectionStart,selectionEnd);
            }
           // int sx=(int)(selectionStart*w)+getParent().getBounds().width;
           // int sy=(int)(selectionEnd*h)+getParent().getBounds().height;
        }
    }
    
    public class SelectionRectangleFigure extends AlphaLayerFigure{

        /**
         * @param color
         * @param alpha
         */
        public SelectionRectangleFigure(Color color, int alpha) {
            super(color, alpha);
           
           
        }
        
        public void setScale(double w,double h){
            if(selectionFigure.isVisible()){
            setRectangleRealValue(selectRectangle);
            }
           // int sx=(int)(selectionStart*w)+getParent().getBounds().width;
           // int sy=(int)(selectionEnd*h)+getParent().getBounds().height;
        }
    }
    
    public class ScrollHorizontalMove implements SelectionListener{

        /* (non-Javadoc)
         * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
         */
        public void widgetSelected(SelectionEvent e) {
            scrollToX(getHorizontalBar().getSelection());
        }

        /* (non-Javadoc)
         * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
         */
        public void widgetDefaultSelected(SelectionEvent e) {
            // TODO Auto-generated method stub
            
        }
        
    }
    
   
    public double[] zoomValues=new double[]{0.0078125,0.015625,0.03125,0.0625,0.125,0.25,0.5,1,2,4};
    public int zoomDefaultSelection=5;//0.25
   
    /*
     * root (null Layout)
     * -background same size as canvas (null Layout)
     * -inPanel  - 	same width as canvas(null Layout)		you can add custom
     * --resisableFigure (stackLayout)
     * ---flowFigure
     * ---memoryFigure
     * --selectionFigure
     * --locationFigure
     * -foreground same size as canvas (null Layout)
     */
    public WaveFormFigureCanvas(Composite parent) {
        
        
        super(parent,SWT.H_SCROLL|SWT.NO_REDRAW_RESIZE);
        shell=getShell();
        
        zoom.setZooms(zoomValues);
        zoom.setZoomIndex(zoomDefaultSelection);
        lws=new LightweightSystem(this);
        this.getHorizontalBar().addSelectionListener(new ScrollHorizontalMove());
        this.getHorizontalBar().setIncrement(20);//
        rootFigure = new EmptyFigure();
        
        lws.setContents(rootFigure);
       
        backgroundFigure = new EmptyFigure();
        rootFigure.add(backgroundFigure);
        
        inPanel = new DoubleScalableFigure(1,1);
        inPanel.setLayoutManager(null);
        
        resisableFigure=new DoubleScalableFigure(1,1);
        resisableFigure.setLayoutManager(new StackLayout());
        inPanel.add(resisableFigure);
        
        
        rootFigure.add(inPanel);
        //inPanel.setLayoutManager(new StackLayout());

       // panel.setLayoutManager(new StackLayout());
        flowFigure = new ChainedFlowFigure();
        
        resisableFigure.add(flowFigure);
        
        
        memoryFigure = new MemoryFigure();
        
        resisableFigure.add(memoryFigure);
        
        selectionFigure=new DraggableAlaphaLayerFigure(ColorConstants.gray,128);
        //selectionFigure.setColor(ColorConstants.gray);
        selectionFigure.setVisible(false);
        inPanel.add(selectionFigure);
        
        //what is this?
        selectionRectangleFigure=new SelectionFigure(ColorConstants.white,128);
        //selectionFigure.setColor(ColorConstants.gray);
        selectionRectangleFigure.setVisible(false);
        inPanel.add(selectionRectangleFigure);
        
        
        locationFigure=new LocationFigure();
        inPanel.add(locationFigure);
        
        
        
        
        //foreground fixed.
        foregroundFigure = new EmptyFigure();
        foregroundFigure.setVisible(true);
        rootFigure.add(foregroundFigure);
        
        
        
       //panel.setBackgroundColor(ColorConstants.darkGray);
        this.addMouseListener(new CanvasMouse());
        this.addMouseMoveListener(new CanvasMouseMotion());//?
        foregroundFigure.addMouseMotionListener(new FigureMouseMotion());
        this.addControlListener(new ResizeCheck());
        
        foregroundFigure.addMouseListener(new FigureMouse());//why?
        
        
        popUpMenu = new Menu(shell, SWT.POP_UP);
		this.setMenu(popUpMenu);
		
	
    }
    
    /*
     * don't call this after composite initialized.
     * use zoomUp or zoomDown
     */
    public Zoom getZoomControler(){
    	return zoom;
    }
    public double getZoom(){
        return zoom.getZoom();
    }
    //this is layerable figure
    //resizableFigure is stackLayout
    public DoubleScalableFigure getInFigure(){
        return resisableFigure;
    }
    public Figure getRootFigure(){
    	return rootFigure;
    }
    public void removeWaveForm(){
        flowFigure.removeAll();
        byteLength=0;
    }
    public void setZoom(double targetZoom,boolean forceZoom){
    	 double currentZoom=zoom.getZoom();
    	if(targetZoom!=currentZoom || forceZoom){
    		zoom.setZoomValue(targetZoom);
    		double value=zoom.getZoom();
            log.info("zoom:"+value);
            inPanel.setScale(value,1);
            resize();
            
            int cx=getCurrentMouseValue();
            log.trace("currentV:"+cx+",half-client="+this.getClientArea().width/2+",scroll="+getHorizontalBar().getSelection());
            scrollToX(cx-this.getClientArea().width/2+this.getHorizontalBar().getSelection());
    	}
    }
    public void zoomUp(){
        double bs=getHorizontalBar().getSelection()/(getHorizontalBar().getMaximum()-getHorizontalBar().getThumb());
        double currentZoom=zoom.getZoom();
        
        zoom.zoomUp();
        
        
        if(currentZoom!=zoom.getZoom()){
        double value=zoom.getZoom();
        log.info("zoom:"+value);
        inPanel.setScale(value,1);
        resize();
        
        int cx=getCurrentMouseValue();
        log.trace("currentV:"+cx+",half-client="+this.getClientArea().width/2+",scroll="+getHorizontalBar().getSelection());
        //int newb=(int)((getHorizontalBar().getMaximum()-getHorizontalBar().getThumb())*bs);
        
        //TODO support move mouse selection is center
        scrollToX(cx-this.getClientArea().width/2+this.getHorizontalBar().getSelection());
        //scrollToX(0);
        }
    }
    
    public void zoomDown(){
        //int select=getHorizontalBar().getSelection();
        double bs=getHorizontalBar().getSelection()/(getHorizontalBar().getMaximum()-getHorizontalBar().getThumb());
        //log.info("selection-before:"+select);
        double currentZoom=zoom.getZoom();
        
        zoom.zoomDown();
        
        
        if(currentZoom!=zoom.getZoom()){
        double value=zoom.getZoom();
        inPanel.setScale(value,1);
        resize();
       
       int newb=(int)((getHorizontalBar().getMaximum()-getHorizontalBar().getThumb())*bs);
       
       if(getClientArea().width>getWaveFormSize()){
           log.info("disable-scrollbar:client-size="+getClientArea().width+",waveformsize="+getWaveFormSize()+",zoom="+zoom.getZoom()+",bytelength="+byteLength+",currentzoom="+currentZoom);
           getHorizontalBar().setEnabled(false);
           scrollToX(0);
           
       }else{
       // scrollToX(newb);
        
        int cx=getCurrentMouseValue();
        log.info("currentV:"+cx+",half-client="+this.getClientArea().width/2+",scroll="+getHorizontalBar().getSelection());
        //int newb=(int)((getHorizontalBar().getMaximum()-getHorizontalBar().getThumb())*bs);
        
        scrollToX(cx-this.getClientArea().width/2+this.getHorizontalBar().getSelection());
        
       }
        }
    }
    
    public class ResizeCheck implements ControlListener{

        /* (non-Javadoc)
         * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent)
         */
        public void controlMoved(ControlEvent e) {
            // TODO Auto-generated method stub
            log.trace("moved");
        }

        /* (non-Javadoc)
         * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
         */
        public void controlResized(ControlEvent e) {
            
            
            
            log.info("client:"+getClientArea());
            //inPanel.setBounds(new Rectangle(this.getClientArea()));
            backgroundFigure.setBounds(new Rectangle(getClientArea()));
            foregroundFigure.setBounds(new Rectangle(getClientArea()));
            
            rescrollbar();
        }
        
    }
    
    public void rescrollbar(){
        int waveFormSize=getWaveFormSize();
       // log.trace("waveform:"+waveFormSize);
        log.trace("client:"+this.getClientArea());
        log.trace("before-horizontalbar:"+getHorizontalBar().getMaximum()+","+getHorizontalBar().getThumb());
        if(getClientArea().width>waveFormSize){
            getHorizontalBar().setEnabled(false);
            scrollToX(0);
            
        }else{
        getHorizontalBar().setMaximum(waveFormSize);//first
        getHorizontalBar().setThumb(this.getClientArea().width);
        
        getHorizontalBar().setEnabled(true);
        }
        log.trace("after-horizontalbar:"+getHorizontalBar().getMaximum()+","+getHorizontalBar().getThumb());
    }
    public void moveCurrentPosition(long tx){
     //TODO location must be inside.
     //   log.info("move:"+tx);
       setCurrentRealValue(tx);
       int x=getCurrentMouseValue()-inPanel.getBounds().x-getHorizontalBar().getSelection();
      // log.info("moveLocation:"+x+","+(this.getHorizontalBar().getSelection()-autoScrollMargin));
      // log.info("inpanel-bounds:"+inPanel.getBounds());
       
        if(x>getClientArea().width-autoScrollMargin){
            log.info("auto-x:tx="+tx+",x="+x+",inPanel.x="+inPanel.getBounds().x+",client-width="+getClientArea().width+",scroll="+getHorizontalBar().getSelection());
            autoScroll(1);
        }else if(x<autoScrollMargin){
            autoScroll(-1);
        }else{
            log.trace("no scroll");
        }
        
        //locationFigure.move(x);
    }
    
    public long getCurrentRealValue(){
        return current;
    }
    public int getCurrentMouseValue(){
        return realToCanvasValue(current);
    }
    public int getLastPosition(){
        int margin=5;//for bug for last play.//i don know still margin need?
        log.info("lastPosition:bytelength:"+byteLength+",baseMemorypixel="+baseMemoryPixel+",splitframe"+splitframe+",margin"+margin);
        return (int)(byteLength*(1000*splitframe))+margin;	
    }
    public int getXByte(int x){
        if(x<0){
            return 0;
        }
        
        
        int figureIndex=x/256;
        
        int at=x%256;
        at=Math.max(0,at);
        if(figureIndex>=flowFigure.getChildren().size()){
            return 0;
        }
        WaveFormFigure figure=(WaveFormFigure)flowFigure.getChildren().get(figureIndex);
        if(figure!=null){
            if(at<figure.getLength()){
                return figure.getByteAt(at);
            }
            
        }
        return 0;
    }

    
    public void loadEmpty(long durationMillisecond){
      
       // List list=WaveFormUtils.toWavValue(perms,file,256);
        log.info("ms:"+WaveUtils.toMillisecondLabel(durationMillisecond));
        List byteList=WaveFormUtils.toWavEmptyValue(splitframe,durationMillisecond,perFigure);
        
        log.info("listSize:"+byteList.size());
        
        
        setByteData(byteList);
       //flowFigure.getLayoutManager().layout(flowFigure);
      
       //log.trace(flowFigure.getSize());
       
       
    }
    
    private Canceller canceller;
    
    public List loadByteList(AudioInputStream input){
    	
        canceller=new Canceller();
        List byteList=WaveFormUtils.toWavValue(splitframe,input,perFigure,canceller);
        log.trace("listSize:"+byteList.size());
        return byteList;
    }
    
    public void loadWave(AudioInputStream input){
       
        
        setByteData(loadByteList(input));
        
        
    }
    
 
    //flowFigure contain waveforms;
    public Figure getFlowFigure(){
    	return flowFigure;
    }
    
    public void removeAll(){
    	flowFigure.removeAll();
    }
    public void setByteData(List byteList){
        flowFigure.removeAll();
        log.trace("removeAll");
        byteLength = 0;
        
        for(int i=0;i<byteList.size();i++){
            byte[] bt=(byte[])byteList.get(i);
            //log.infobt:"+bt.length);
            WaveFormFigure wf=new WaveFormFigure(bt,waveFormHeight);
            flowFigure.addChainedFigure(wf);
            wf.setForegroundColor(ColorConstants.orange);
            byteLength+=bt.length;
        }
        log.warn("bt-length:"+byteLength);
        resize();
    }
    
    public void loadWave(File file){
    	
        log.trace("open:"+file);
        try {
        	AudioFileFormat format=AudioSystem.getAudioFileFormat(file);
        	maxAudioTime=(long) (format.getFrameLength()/format.getFormat().getSampleRate()*1000);
        	setSampleRate((int)(format.getFormat().getSampleRate()));
            loadWave(AudioSystem.getAudioInputStream(file));
        } catch (FileNotFoundException e) {
            
            log.warn(e);
        } catch (UnsupportedAudioFileException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    

    public int getWaveFormSize(){
        return (int)(byteLength*zoom.getZoom());
    }
    
    //got problem
    public int toPanelHeight(){
    	if(panelHeight>0){
    		return panelHeight;
    	}else{
    		return this.getClientArea().height;
    	}
    }
    public void resize(){
        
        int waveFormSize=getWaveFormSize();
       
        inPanel.setSize((int)(waveFormSize),toPanelHeight());//here the must height
        locationFigure.setSize(locationFigure.getSize().width,toPanelHeight());//
        resisableFigure.setSize(waveFormSize,toPanelHeight());
        
        
        //flowFigure.setSize((int)(waveFormSize),256);
        //memoryFigure.setSize((int)(waveFormSize),256);
        
        flowFigure.setScale(zoom.getZoom(),1);
        
        //this.setSize(waveFormSize,this.getBounds().height);
        log.info("zoom:"+zoom.getZoom());
        log.info("waveFormSize:"+waveFormSize);
        inPanel.revalidate();
        log.info("panelSize:"+inPanel.getClientArea());
        
       
        
        
        //inPanel.setBounds(new Rectangle(this.getClientArea()));
        
        backgroundFigure.setBounds(new Rectangle(this.getClientArea()));
        foregroundFigure.setBounds(new Rectangle(this.getClientArea()));
         //log.trace("-----------------foreground:"+foregroundFigure.getClientArea());
        
rescrollbar();
        rootFigure.revalidate();
        
        log.info("root:"+rootFigure.getClientArea());
        
    }
    

    
 

   
    
    public class LocationFigure extends DoubleScalableFigure{
        int markSize=2;//why 2
         int value=128;
        int height=256;
       
        public LocationFigure(){
            super(1,1);
            setSize(1+markSize*2,toPanelHeight());
            setForegroundColor(ColorConstants.gray);
            setBackgroundColor(ColorConstants.gray);
        }
        
        public void setScale(double w,double h){
            
            wzoom=w;
            setCurrentRealValue(current);
            
        }
        /**
         * @return
         */
        public int getCurrent() {
            // TODO Auto-generated method stub
            return getLocation().x+markSize;
        }
        /**
         * @param location
         */
        public void move(int location) {
        	//log.info("location:"+location);
            if(waveFormListener!=null){
                waveFormListener.changeCurrent((int)(current*1000/baseMemoryPixel));
            }
            setLocation(new Point(location-markSize,0));
            setValue(getXByte(location));
        }
        public void paintFigure(Graphics g) {
            
            g.drawLine(markSize+getClientArea().x,getClientArea().y,markSize+getClientArea().x,getClientArea().height);
		    
            //this case oval dont need
            //g.fillOval(getClientArea().x,getClientArea().y+value,markSize*2,markSize*2);
            
		   // log.trace(getClientArea().y+value);
            
            //log.trace("***");
            //log.trace("memory:"+memoryFigure.isVisible()+","+memoryFigure.getClientArea());
            //log.trace("inPanel:"+inPanel.isVisible()+","+inPanel.getClientArea());
            //log.trace("resisableFigure:"+resisableFigure.isVisible()+","+resisableFigure.getClientArea());
		    }
        public void setValue(int v){
            value=height/2+v;//TODO support resize.
        }
        
	    
    }
    
    
    public void scrollToX(int tx){
        int x=Math.max(tx,0);
        x=Math.min(x,this.getHorizontalBar().getMaximum()-this.getHorizontalBar().getThumb());
        this.getHorizontalBar().setSelection(x);
        
        log.trace("scrollToX:"+x+",size"+getHorizontalBar().getMaximum()+",thumb="+getHorizontalBar().getThumb());
        //flowFigure.setLocation(new Point(-x,0));
       
        inPanel.setLocation(new Point(-x,0));
        
        log.trace("location:"+locationFigure.getClientArea());
        
        log.info("bound:"+inPanel.getBounds());
    }
    
    
    public void autoScroll(int duration){
       
        log.info("autoScroll:"+duration);
        int v=getHorizontalBar().getIncrement();
       // log.infozoomUp:"+zoom.getZoom());
        if(zoom.getZoom()==1){//zoomed scroll has a problem.
        	v*=2;
        }else if(zoom.getZoom()>1){
        	v*=2*zoom.getZoom();
        }
        log.info(zoom.getZoom()+","+v);
        scrollToX(getHorizontalBar().getSelection()+v*duration);
    }
    

    
    
    
    public class CallScroll implements Runnable{

        int duration;
        
        public CallScroll(int duration){
            this.duration=duration;
        }
        /* (non-Javadoc)
         * @see java.lang.Runnable#run()
         */
        public void run() {
            // TODO Auto-generated method stub
            autoScroll(duration);
            mouseUp+=duration*getHorizontalBar().getIncrement();
            mouseUp=Math.max(0,mouseUp);
            mouseUpTime=canvasToRealValue(mouseUp);
            updateSelect();
            setCurrentMouseValue(mouseUp);
        }
        
    }
    
    public class Drugging implements Runnable{
        private boolean stopped;
        private int duration;
        public Drugging(int duration){
            this.duration=duration;
        }
        public void setStopped(boolean bool){
            stopped=bool;
        }
        public void run(){
            while(pressed && !stopped){
                
                
               log.trace("drugging:"+duration);
               shell.getDisplay().syncExec(new CallScroll(duration));
               
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            log.trace("druggin-finish");
        }
    }
    
    private Drugging drugging;
    public class CanvasMouseMotion implements MouseMoveListener{

        /* (non-Javadoc)
         * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
         */
        public void mouseMove(MouseEvent e) {
            int maxwidth=resisableFigure.getBounds().x+resisableFigure.getBounds().width;
            
            if(e.x<maxwidth && e.x>0){
               //work outside only.
               return;
            }
           // log.trace("mosueMove");
            
            if(e.stateMask==524288){//DRUG
                log.info("canvas-mosue-dragged");
                
                if(drugging!=null){
                    drugging.setStopped(true);
                }
                
                
                
                /*
                while(!getShell().isDisposed()){			
        			if (!getShell().getDisplay().readAndDispatch ()){
        			    log.trace("pressed");
        			    getShell().getDisplay().sleep ();
        			    if(!pressed){
        			        log.trace("break");
        			        break;
        			    }
        				  }
        		}*/
                
               
                mouseUp=e.x;
                mouseUpTime=canvasToRealValue(mouseUp);
                log.info("mosue-up:"+mouseUp);
                
                if(mode==NORMAL){
                    
                    setCurrentMouseValue(e.x);
                }
                
                
                
                updateSelect();
               
               
                if(getHorizontalBar().isEnabled()){
                if(mouseUp>mouseDown 	//for edge selection.
                        && e.x>getClientArea().width-autoScrollMargin){
                    drugging=new Drugging(1);
                    Thread t=new Thread(drugging);
                    t.start();
                    
                }else if(mouseUp<mouseDown
                        && e.x<autoScrollMargin){
                    drugging=new Drugging(-1);
                    Thread t=new Thread(drugging);
                    t.start();
                    
                }
                }
                /*
               
                if(mouseUp>mouseDown 	//for edge selection.
                        && e.x>getClientArea().width-autoScrollMargin){
                    //log.trace("scroll: current="+getHorizontalBar().getSelection()+","+(getHorizontalBar().getMaximum()-getHorizontalBar().getThumb()));
                    autoScroll(1);
                    log.trace("scroll-end");
                    //mouseUp=getHorizontalBar().getSelection();
                    if(getHorizontalBar().getSelection()>=getHorizontalBar().getMaximum()-getHorizontalBar().getThumb()){
                        log.trace("break over");
                        break;
                    }
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }else if(mouseUp<mouseDown
                        && e.x<autoScrollMargin){
                    if(getHorizontalBar().getSelection()>0){
                       
                    autoScroll(-1);
                    
                    //mouseUp=getHorizontalBar().getSelection();
                    
                    }else{
                        //only can scroll.
                        break;
                    }
                    
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }else{
                   break;
                }
               */
                
                
                
                }
                
              
            
           
        }

   
    
    }
    
    /*
     * show mouse dragged animation
     */
    public class FigureMouseMotion implements MouseMotionListener{


        /* (non-Javadoc)
         * @see org.eclipse.draw2d.MouseMotionListener#mouseDragged(org.eclipse.draw2d.MouseEvent)
         */
        public void mouseDragged(org.eclipse.draw2d.MouseEvent e) {

            log.trace("figure-mouse-dragged");
            
            if(drugging!=null){
                drugging.setStopped(true);
            }
            
            if(!pressed){
                //log.info("maybe wrong select");
            	if(dragItemControler!=null && dragItemControler.isSelected()){
            		 int realX=-inPanel.getBounds().x+e.x;
                     int realY=-inPanel.getBounds().y+e.y;
            		dragItemControler.dragged(realX,realY);
            	}
                return ;
            }
            
            
           
           
            mouseUp=e.x;
            mouseUpTime=canvasToRealValue(mouseUp);
            
            if(mode==NORMAL){
                
                setCurrentMouseValue(e.x);
            }
            
            
            
            updateSelect();
           
           
            if(getHorizontalBar().isEnabled()){
            if(mouseUp>mouseDown 	//for edge selection.
                    && e.x>getClientArea().width-autoScrollMargin){
                drugging=new Drugging(1);
                Thread t=new Thread(drugging);
                t.start();
                
            }else if(mouseUp<mouseDown
                    && e.x<autoScrollMargin){
                drugging=new Drugging(-1);
                Thread t=new Thread(drugging);
                t.start();
                
            }
            }
        }
        

        /* (non-Javadoc)
         * @see org.eclipse.draw2d.MouseMotionListener#mouseEntered(org.eclipse.draw2d.MouseEvent)
         */
        public void mouseEntered(org.eclipse.draw2d.MouseEvent me) {
            // TODO Auto-generated method stub
            
        }

        /* (non-Javadoc)
         * @see org.eclipse.draw2d.MouseMotionListener#mouseExited(org.eclipse.draw2d.MouseEvent)
         */
        public void mouseExited(org.eclipse.draw2d.MouseEvent me) {
            // TODO Auto-generated method stub
            
        }

        /* (non-Javadoc)
         * @see org.eclipse.draw2d.MouseMotionListener#mouseHover(org.eclipse.draw2d.MouseEvent)
         */
        public void mouseHover(org.eclipse.draw2d.MouseEvent me) {
            // TODO Auto-generated method stub
            
        }

        /* (non-Javadoc)
         * @see org.eclipse.draw2d.MouseMotionListener#mouseMoved(org.eclipse.draw2d.MouseEvent)
         */
        public void mouseMoved(org.eclipse.draw2d.MouseEvent me) {
            // TODO Auto-generated method stub
            
        }

   
    
    }
    
    public void updateSelect(){
        
        int p1=mouseUp;
        int p2=mouseDown;
        if(mouseUp>mouseDown){
            p1=mouseDown;
            p2=mouseUp;
        }
        
        
        //log.trace(""+p1+","+p2);
        
        if(p2>p1+1){
            //select(p1,p2);//mouse base
        	long start=mouseUpTime;
        	long end=mouseDownTime;
        	 if(mouseUpTime>mouseDownTime){
        		 start=mouseDownTime;
                 end=mouseUpTime;
             }
            setSelectRealValue(start, end);
            if(timeSelectionListener!=null){
          	   timeSelectionListener.timeSelectionChanged(new TimeSelection(selectionStart,selectionEnd));
             }
        }else{
            unselect();
        }
    }
    
    
    public static interface TimeSelectionListener {
    	public void timeSelectionChanged(TimeSelection selection);
    }
    
    private TimeSelectionListener timeSelectionListener;
    
    /**
     * 
     */
    public void unselect() {
        
        selectionFigure.setVisible(false);
       if(timeSelectionListener!=null){
    	   timeSelectionListener.timeSelectionChanged(new TimeSelection(0,0));
       }
    }
    
    /**
     * @param p1
     * @param p2
     */
    private void select(int p1, int p2) {
        setSelectMouseValue(p1,p2);
        log.trace("select:"+selectionFigure.getClientArea());
       
    }
    
    public static class TimeSelection{
    	private long start;
    	private long end;
		public long getEnd() {
			return end;
		}
		public void setEnd(long end) {
			this.end = end;
		}
		public long getStart() {
			return start;
		}
		public void setStart(long start) {
			this.start = start;
		}
    	public TimeSelection(long start,long end){
    		this.start=start;
    		this.end=end;
    	}
    	public boolean equals(Object object){
    		if(object instanceof TimeSelection){
    			TimeSelection ts=(TimeSelection) object;
    			return ts.getStart()==this.getStart()&&ts.getEnd()==this.getEnd();
    		}
    		return false;
    	}
    	  public String toString(){
    	    	return start+","+end;
    	    }
    	    public boolean intersect(long targetStart,long targetEnd){
    	    	boolean ret= targetStart>=this.start && targetStart<=this.end || targetEnd>=this.start && targetEnd<=this.end;
    	    	if(ret){
    	    		return true;
    	    	}
    	    	
    	    	return this.start>=targetStart && this.start<targetEnd || this.end>=targetStart && this.end<targetEnd ;
    	    }
    }

    public Rectangle getSelectionMoveRect(){
    	  if(selectionFigure.isVisible()){
          return selectionFigure.getMovableRect();
          }else{
              return null;
          }
    }
    public TimeSelection getSelection(){
        if(selectionFigure.isVisible()){
        	log.info("selectionFigure:"+selectionFigure.getClientArea());
        return new TimeSelection(selectionStart,selectionEnd);    //is long time OK?
        }else{
            return null;
        }
       
      
    }
  
    /*
    public Point getSelection(){
        if(selectionFigure.isVisible()){
            log.info(""+inPanel.getLocation().x);
            return new Point(selectionFigure.getLocation().x,selectionFigure.getLocation().x+selectionFigure.getSize().width);
        }else{
            return null;
        }
       
    }*/
    boolean pressed;
    int mouseDown;
    int mouseUp;
    long mouseUpTime;
    long mouseDownTime;
    private Figure backgroundFigure;
    
    public static class EmptyFigure extends Figure {
        
        public EmptyFigure(){
         
        }
        
        public void paintFigure(Graphics g) {
        	//log.info(this.getClientArea());
        }
        
    }
    
    public class MemoryFigure extends Figure{
    	
        private SimpleGraphMemoryPainterDraw2D painter;
        public MemoryFigure(){
            painter = new  SimpleGraphMemoryPainterDraw2D();
            painter.setVerticalGraph(false);
            painter.setGraphPlus(true);
            painter.setGridPlus(true);
            painter.setLabelProvider(new LabelProvider());
            painter.setLineColor(ColorConstants.darkGray);
        }
        public void paintFigure(Graphics g) {
            
            log.trace("memoryfigure:"+g.getClip(this.getClientArea()));
            painter.draw(g,this.getClientArea().x,this.getClientArea().y,flowFigure.getSize().width);
         }
        
       
        public SimpleGraphMemoryPainterDraw2D getPainter() {
            return painter;
        }
        public void setPainter(SimpleGraphMemoryPainterDraw2D painter) {
            this.painter = painter;
        }
    }
    
    public void setSelectMouseValue(int start,int end){
    	if(start==end){
    		setCurrentMouseValue(start);
    		unselect();
    		return;
    	}
        selectionStart=canvasToRealValue(start);
        selectionEnd=canvasToRealValue(end);
        selectionFigure.setLocation(new Point(start,0));
        selectionFigure.setSize(end-start,toPanelHeight());//TODO change height;
        selectionFigure.setVisible(true);
        
        if(timeSelectionListener!=null){
     	   timeSelectionListener.timeSelectionChanged(new TimeSelection(selectionStart,selectionEnd));
        }
    }
    
    public void setSelectRealValue(long start,long end){
    	if(start==end){
    		setCurrentRealValue(start);
    		unselect();
    		return;
    	}
        selectionStart=start;
        selectionEnd=end;
   //     int ms=(int)(start*zoom.getZoom())+getParent().getBounds().x;
     //   int me=(int)(end*zoom.getZoom())+getParent().getBounds().x;
        int ms=realToCanvasValue(start);
        int me=realToCanvasValue(end);
        selectionFigure.setLocation(new Point(ms,0));
        selectionFigure.setSize(me-ms,toPanelHeight());//TODO change height;
        selectionFigure.setVisible(true);
        //why here is no event listener
    }
    
    public Rectangle setRectangleRealValue(){
        if(this.selectRectangle!=null){
            return selectionRectangleFigure.getBounds();
        }else{
            return null;
        }
    }
    public void setRectangleRealValue(Rectangle rectangle){
        if(rectangle!=null){
            //log.info("selectrect:"+rectangle);
            this.selectRectangle=rectangle;
            
            int dx=realToCanvasValue(rectangle.x);
            int dwidth=realToCanvasValue(rectangle.x+rectangle.width);
            //log.info("new-value:x="+dx+",width="+dwidth);
            selectionRectangleFigure.setLocation(new Point(dx,rectangle.y));
            selectionRectangleFigure.setSize(dwidth-dx,rectangle.height);
            selectionRectangleFigure.setVisible(true);
        }else{
            selectionRectangleFigure.setVisible(false);
        }
    }
    
    public void setCurrentRealValue(long value){
        if(log.isTraceEnabled()){
        log.trace("value:"+value);
        log.trace("zoom:"+zoom.getZoom());
        log.trace("bound:"+inPanel.getBounds().x);
        log.trace("location:"+((int)(value*zoom.getZoom())+inPanel.getBounds().x));
        }
        current=value;//real value.
        locationFigure.move(realToCanvasValue(value));
    }
    
    public void setCurrentMouseValue(int value){
        current=canvasToRealValue(value);
        locationFigure.move(value);
        log.info("current-time:"+current);
        log.trace("location:"+locationFigure.getClientArea());
    }
    
    /**
     * 
     * @param millisecond time
     * @return
     */
    public int realToCanvasValue(long value){
    	
    	int x=(int)(value*zoom.getZoom()/(1000*splitframe))+inPanel.getBounds().x;
    	log.info("value="+value+",x="+x);
        return x;
    }
    
    public int getScrolledCanvasSize(){
    	return -inPanel.getBounds().x;
    }
    
    /*TODO
     * i think this has problem.especially baseMemoryPixel
     * why value based on baseMemoryPixel.
     */
    public long canvasToRealValue(int value){
        //return (int)((double)(value-inPanel.getBounds().x)/zoom.getZoom()*baseMemoryPixel*splitframe);
    	long ms=(int)((value-inPanel.getBounds().x)/zoom.getZoom()*(1000*splitframe));
    	//log.infoms:"+ms);
    	
    	
    	//verify  value
    	if(ms<0){
    		ms=0;
    	}
    	if(ms>maxAudioTime){
    		ms=maxAudioTime;
    	}
    	return ms;
    }
    
/**
 * 
 * @param canvas pixel size
 * @return millisecond time
 */
    public long pixelToMillisecond(int value){
        long ms=(int)((value)/zoom.getZoom()*(1000*splitframe));
    	return ms;
    }
    
    public class LabelProvider implements GraphLabelProvider{
    	private int digit=6;
        /* (non-Javadoc)
         * @see org.jpn.xucker.rcp.draw2d.GraphLabelProvider#getLabel(org.jpn.xucker.rcp.draw2d.SimpleGraphMemoryPainterDraw2D, int)
         */
        public String getLabel(SimpleGraphMemoryPainterDraw2D painter, int index) {
            //default memory 100pixel pergraph
            double ms=index/zoom.getZoom()*memoryFigure.getPainter().getPerMain()*splitframe;
            BigDecimal bd = new BigDecimal(ms);
            double v = bd.setScale(digit, BigDecimal.ROUND_HALF_UP)
                              .doubleValue();
            return ""+v;
        }
        
    }
    private Figure foregroundFigure;
    private DoubleScalableFigure inPanel;
    private MemoryFigure memoryFigure;
    private int byteLength;
    
    /*
     * this class is temporary.
     * TODO
     * should be interfaced
     */
    public  DragItemControler dragItemControler;
    public static abstract class DragItemControler{
    	protected boolean selected;
    	protected int clickX;
    	protected int clickY;

    	public boolean isSelected(){
    		return selected;
    	}
    	
    	public abstract void dragged(int mouseX, int mouseY) ;

		public abstract void mouseDown(int mouseX,int mouseY);
    	
    	
    	public abstract void mouseUp(int mouseX,int mouseY);
    }
    
    public class FigureMouse implements org.eclipse.draw2d.MouseListener{

        /* (non-Javadoc)
         * @see org.eclipse.draw2d.MouseListener#mousePressed(org.eclipse.draw2d.MouseEvent)
         */
        public void mousePressed(org.eclipse.draw2d.MouseEvent me) {
        	
        	if(me.button!=1){
        		return;
        	}
            
        	if(dragItemControler!=null){
        		 int realX=-inPanel.getBounds().x+me.x;
                 int realY=-inPanel.getBounds().y+me.y;
        	dragItemControler.mouseDown(realX,realY);//for special items
        	}
        	
        	
            if(dragItemControler!=null && dragItemControler.isSelected()){
            	
            	return;
            }
            
            //log.trace("***figure-press:"+me.isConsumed());
            //log.trace("figure-x:"+me.x);
            //log.trace("canvas-x:"+e.x);
            setCurrentMouseValue(me.x);
            //log.trace("mouse-down");
            mouseDown=me.x;
            mouseDownTime=canvasToRealValue(mouseDown);
            pressed=true;
        }

        /* (non-Javadoc)
         * @see org.eclipse.draw2d.MouseListener#mouseReleased(org.eclipse.draw2d.MouseEvent)
         */
        public void mouseReleased(org.eclipse.draw2d.MouseEvent me) {
        	if(me.button!=1){
        		return;
        	}
        	
        	if(dragItemControler!=null){
        		 int realX=-inPanel.getBounds().x+me.x;
                 int realY=-inPanel.getBounds().y+me.y;
        	dragItemControler.mouseUp(realX,realY);//for special items
        	}
            log.trace("figure-mouse-release");
            //log.trace("***figure-release:"+me.isConsumed());
           
            
            if(pressed==true){//bug for call select from menu select.
                mouseUp=me.x;
                mouseUpTime=canvasToRealValue(mouseUp);
                log.trace("mouseReleased");
                updateSelect();
            }
            
            pressed=false;
        }

        /* (non-Javadoc)
         * @see org.eclipse.draw2d.MouseListener#mouseDoubleClicked(org.eclipse.draw2d.MouseEvent)
         */
        public void mouseDoubleClicked(org.eclipse.draw2d.MouseEvent me) {
            // TODO Auto-generated method stub
        	log.trace("figure double clicked");
        }
        
    }
    
    private MouseDoubleClickListener mouseDoubleClickListener;
	private Menu popUpMenu;
    public static interface MouseDoubleClickListener{
    	public void mouseDoubleClicked(long millisecond);
    }
    public class CanvasMouse implements MouseListener{

       
        public void mouseDoubleClick(MouseEvent e) {
        	log.trace("canvas double clicked");
        	long ms=canvasToRealValue(e.x);
        	if(mouseDoubleClickListener!=null){
        		mouseDoubleClickListener.mouseDoubleClicked(ms);
        	}
        }

   
        public void mouseDown(MouseEvent e) {
        	
            log.trace("canvas-mouse-release");
            int maxwidth=resisableFigure.getBounds().x+resisableFigure.getBounds().width;
            if(e.x>maxwidth){
                setCurrentMouseValue(maxwidth);
                mouseDown=maxwidth;
                pressed=true;
            }
            /*
            log.trace("canvas-x:"+e.x);
            setCurrentMouseValue(e.x);
            mouseDown=e.x;
            pressed=true;
            */
           
        }
        
        
        
     

       

        public void mouseUp(MouseEvent e) {
        	
            log.trace("canvas-mouse-release");
            /*
           if(pressed){
           mouseUp=e.x;
           
          updateSelect();
           pressed=false;
           }*/
            pressed=false;//bug fix for still drugging.
        }
        
      

   
    }
    public WaveFormListener getWaveFormListener() {
        return waveFormListener;
    }
    public void setWaveFormListener(WaveFormListener waveFormListener) {
        this.waveFormListener = waveFormListener;
    }
    public double getSplitFrame() {
        return splitframe;
    }
    /*
     * pixel time
     */
    public void setSplitFrame(double splitframe) {
        this.splitframe = splitframe;
    }
    public int getBaseMemoryPixel() {
        return baseMemoryPixel;
    }
    public void setBaseMemoryPixel(int baseMemoryPixel) {
        this.baseMemoryPixel = baseMemoryPixel;
    }
    public Canceller getCanceller() {
        return canceller;
    }
	public MouseDoubleClickListener getMouseDoubleClickListener() {
		return mouseDoubleClickListener;
	}
	public void setMouseDoubleClickListener(
			MouseDoubleClickListener mouseDoubleClickListener) {
		this.mouseDoubleClickListener = mouseDoubleClickListener;
	}
	public TimeSelectionListener getTimeSelectionListener() {
		return timeSelectionListener;
	}
	public void setTimeSelectionListener(TimeSelectionListener timeSelectionListener) {
		this.timeSelectionListener = timeSelectionListener;
	}
	
	/*
	 * call CTRL+A directlly
	 * at jp.sourceforge.akjrcp.ui.draw2d.WaveFormFigureCanvas.selectAll(WaveFormFigureCanvas.java:1326)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at org.eclipse.ui.internal.handlers.SelectAllHandler.execute(SelectAllHandler.java:133)
	at org.eclipse.ui.internal.handlers.HandlerProxy.execute(HandlerProxy.java:151)
	at org.eclipse.core.commands.Command.executeWithChecks(Command.java:461)
	at org.eclipse.core.commands.ParameterizedCommand.executeWithChecks(ParameterizedCommand.java:424)
	at org.eclipse.ui.internal.handlers.HandlerService.executeCommand(HandlerService.java:160)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.executeCommand(WorkbenchKeyboard.java:466)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.press(WorkbenchKeyboard.java:799)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.processKeyEvent(WorkbenchKeyboard.java:846)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.filterKeySequenceBindings(WorkbenchKeyboard.java:564)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard.access$3(WorkbenchKeyboard.java:506)
	at org.eclipse.ui.internal.keys.WorkbenchKeyboard$KeyDownFilter.handleEvent(WorkbenchKeyboard.java:122)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
	at org.eclipse.swt.widgets.Display.filterEvent(Display.java:982)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:927)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:952)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:937)
	at org.eclipse.swt.widgets.Widget.sendKeyEvent(Widget.java:965)
	at org.eclipse.swt.widgets.Widget.sendKeyEvent(Widget.java:961)
	at org.eclipse.swt.widgets.Widget.wmChar(Widget.java:1275)
	at org.eclipse.swt.widgets.Control.WM_CHAR(Control.java:3346)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:3246)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:4025)
	at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method)
	at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:1932)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2966)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:1930)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1894)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:422)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at jp.sourceforge.akjrcp.akj_betas.Application.run(Application.java:18)
	at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:78)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:177)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:585)
	at org.eclipse.core.launcher.Main.invokeFramework(Main.java:336)
	at org.eclipse.core.launcher.Main.basicRun(Main.java:280)
	at org.eclipse.core.launcher.Main.run(Main.java:977)
	at org.eclipse.core.launcher.Main.main(Main.java:952)
	 */
	public void selectAll() {
		if(maxAudioTime>0){
		setSelectRealValue(0, maxAudioTime);
		if(timeSelectionListener!=null){
			timeSelectionListener.timeSelectionChanged(getSelection());
		}
		}else{
			log.info("zero audio");
		}
	}
	
	public Menu getPopUpMenu() {
		return popUpMenu;
	}
	public int getSampleRate() {
		return sampleRate;
	}
	public void setSampleRate(int sampleRate) {
		this.sampleRate = sampleRate;
	}
	public DragItemControler getDragItemControler() {
		return dragItemControler;
	}
	public void setDragItemControler(DragItemControler dragItemControler) {
		this.dragItemControler = dragItemControler;
	}
	public long getMaxAudioTime() {
		return maxAudioTime;
	}
}
